package com.nvc.homeplan;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;

public class ImageScrollZoom extends Activity {

	// Physical display width and height.
	private static int displayWidth = 0;
	private static int displayHeight = 0;

	private static Display display = null;
	private static Vibrator vibrator = null;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
    	HomePlanApplication application = (HomePlanApplication) getApplication();
    	String imageURL = application.imageURL;
		
		
		display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
		vibrator = ((Vibrator) getSystemService(Context.VIBRATOR_SERVICE));
		setContentView(new SampleView(this));
	}

	private static class SampleView extends View {
		private static Bitmap bmLargeImage = null; // bitmap large enough to scroll
		private static Rect displayRect = null; // rect we display to
		private Rect scrollRect = null; // rect we scroll over our bitmap with
		private float scrollRectX = 0; // current left location of scroll rect
		private float scrollRectY = 0; // current top location of scroll rect
		private float scrollByX = 0; // x amount to scroll by
		private float scrollByY = 0; // y amount to scroll by
		private float startX = 0; // track x from one ACTION_MOVE to the next
		private float startY = 0; // track y from one ACTION_MOVE to the next
		private float downX = 0; // x cached at ACTION_DOWN
		private float downY = 0; // y cached at ACTION_DOWN

		private boolean isLongPress = false; // only want one long press per gesture
		private boolean hasNotMoved = true; // long-press determination

		// Get static method data via static access to ViewConfiguration.
		private static final long tapTime = ViewConfiguration.getTapTimeout();
		private static final long longPressTime = ViewConfiguration.getLongPressTimeout();
		private static int scaledTouchSlopSquared = 0;

		private static final int MSG_LONG_PRESS = 1;

		private boolean zoom = false;
		private float bmRefPtX = 0;
		private float bmRefPtY = 0;

		private static final float ZOOMFACTOR = .8f;
		private static final int MAXZOOMLEVEL = 6;
		private float currentZoom = 1.0f;
		private int zoomDir = 1;
		private int zoomLevel = 0;
		private float scaledWidth = 0;
		private float scaledHeight = 0;

		public SampleView(Context context) {
			super(context);

			// displayWidth and displayHeight will change depending on screen
			// orientation. To get these dynamically, we should hook onSizeChanged().
			// This simple example uses only landscape mode, so it's ok to get them
			// once on startup and use those values throughout.
			// scaledWidth and scaledHeight will be the size of the screen, adjusted
			// by our current zoom value.
			scaledWidth = displayWidth = display.getWidth();
			scaledHeight = displayHeight = display.getHeight();

			displayRect = new Rect(0, 0, displayWidth, displayHeight);
			scrollRect = new Rect(0, 0, displayWidth, displayHeight);

			bmLargeImage = BitmapFactory.decodeResource(getResources(), R.drawable.zoomtestimage);
			
			// Set up a bitmap reference point that matches the center of the screen,
			// so that we can easily track what bitmap point we need to center-and-zoom 
			// around.
			bmRefPtX = displayWidth / 2;
			bmRefPtY = displayHeight / 2;

			// Get non-static method data from ViewConfiguration.
			ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
			scaledTouchSlopSquared = viewConfiguration.getScaledTouchSlop()
					* viewConfiguration.getScaledTouchSlop();
		}

		private Handler messageHandler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				switch (msg.what) {
				// Schedule a long press. This will be canceled if:
				// * gesture finishes (ACTION_UP)
				// * gesture is canceled (ACTION_CANCEL)
				// * we move outside of our 'slop' range
				case MSG_LONG_PRESS:
					handleLongPress();
					break;

				default:
					throw new RuntimeException("handleMessage: unknown message "
							+ msg);
				}
			}
		};

		@Override
		public boolean onTouchEvent(MotionEvent event) {

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				// Remember our initial down event location.
				startX = downX = event.getX();
				startY = downY = event.getY();
				// We will set this once a long-press actually fires.
				isLongPress = false;
				// Assume this is a long-press, it will be canceled either
				// by moving or by canceling the gesture.
				messageHandler.removeMessages(MSG_LONG_PRESS);
				messageHandler.sendEmptyMessageAtTime(MSG_LONG_PRESS, event.getDownTime()
						+ tapTime + longPressTime);
				break;

			case MotionEvent.ACTION_MOVE:
				// A long-press has fired and is in progress. Don't bother with
				// any other processing. A side-effect of this is that we won't
				// be able to do any other movement until we either cancel
				// (ACTION_CANCEL) or finish (ACTION_UP) the current movement.
				if (isLongPress)
					break;

				final float x = event.getX();
				final float y = event.getY();
				scrollByX = x - startX; // calculate move increments
				scrollByY = y - startY;

				if (hasNotMoved) {
					// Have we moved out of the threshold radius of initial
					// user touch?
					final int deltaXFromDown = (int) (x - downX);
					final int deltaYFromDown = (int) (y - downY);
					int distance = (deltaXFromDown * deltaXFromDown)
							+ (deltaYFromDown * deltaYFromDown);
					if (distance > scaledTouchSlopSquared) {
						// We've moved so handle scroll update and cancel
						// long-press.
						hasNotMoved = false;
						messageHandler.removeMessages(MSG_LONG_PRESS);
						startX = x; // reset previous values to latest
						startY = y;
						invalidate();
					}
				} else {
					// This is a move gesture so update our move incrementers
					// for next pass through ACTION_MOVE, and force a redraw
					// with the updated scroll values. We don't need to call
					// .removeMessages() since the only way here is via the if
					// block above which calls it for us.
					startX = x; // reset previous values to latest
					startY = y;
					invalidate();
				}
				break;

			case MotionEvent.ACTION_UP:
				isLongPress = false;
				hasNotMoved = true;
				messageHandler.removeMessages(MSG_LONG_PRESS);
				break;

			case MotionEvent.ACTION_CANCEL:
				isLongPress = false;
				hasNotMoved = true;
				messageHandler.removeMessages(MSG_LONG_PRESS);
				break;
			}
			return true; // done with this event so consume it
		}

		void handleLongPress() {
			// Indicate that a long-press has fired.
			isLongPress = true;
			zoom = true;

			// Execute your long press code here.
			vibrator.vibrate(50);

			// Recenter at current zoom level. Centering occurs before we start
			// the scaling operations at the new zoom level. These are the deltas
			// that translate where we touched the screen (in screen coordinates)
			// to where the bitmap reference point should be moved to in bitmap
			// coordinates. This will center us around the correct point of the
			// bitmap.
			bmRefPtX += (downX - (displayWidth / 2)) * currentZoom;
			bmRefPtY += (downY - (displayHeight / 2)) * currentZoom;

			// This goes away when we implement +/- type zoom controls. For now zoom in a few
			// steps, then zoom back out.
			zoomLevel += zoomDir;
			if (zoomDir == 1) {  //zooming in
				currentZoom *= ZOOMFACTOR;
				if (zoomLevel == MAXZOOMLEVEL)
					zoomDir = -1; 
			} else {  //zooming out
				currentZoom /= ZOOMFACTOR;
				if (zoomLevel == 0) {
					zoomDir = 1; 
					currentZoom = 1.0f; // avoid roundoff accumulation
				}
			}

			// If we are zooming in (our new currentZoom is smaller than our current one) we 
			// have a smaller area that we want to be 'stretched' by the drawBitmap call so
			// it fills the full screen. To zoom out (new currentZoom is larger than the 
			// current one) we have a larger area that will be 'compressed' (by drawBitmap)
			// so that this larger area fits on screen.
			scaledWidth = ((float) displayWidth * currentZoom);
			scaledHeight = ((float) displayHeight * currentZoom);

			// We can't let our bmRefPt go outside it's original bounds, scaled by the current
			// zoom factor, because that would give us negative scroll rectangle coordinates. 
			// The original bounds (set on initialization) were so that the upper left corner
			// matched the screen center.
			// So bmRefPt must stay inside the rectangle formed by this upper left point and
			// a width and height of (bmWidth - current_screen_width) and 
			// (bmHeight - current_screen_height). The current_screen_width is just our 
			// scaledWidth (the original display width scaled by our current zoom value). 
			// Similarly for height. 
			bmRefPtX = clamp(bmRefPtX, scaledWidth/2.0f, bmLargeImage.getWidth()
					- (scaledWidth / 2.0f));
			bmRefPtY = clamp(bmRefPtY, scaledHeight / 2.0f, bmLargeImage.getHeight()
					- (scaledHeight / 2.0f));

			invalidate();
		}

		@Override
		protected void onDraw(Canvas canvas) {
			float newScrollRectX = 0.0f;
			float newScrollRectY = 0.0f;

			if (zoom == true) {
				// We are zooming, so set the scroll rectangle so that it is 
				// centered around bmRefPt (which is now the center of the 
				// screen) with a height and width of the display height and
				// width, scaled by the current zoom value.
				newScrollRectX = bmRefPtX - (scaledWidth / 2.0f);
				newScrollRectY = bmRefPtY - (scaledHeight / 2.0f);
				zoom = false;
			} else {
				// Set our scroll rectangle to reflect the amount we moved,
				// scaled by our current zoom level.
				newScrollRectX = scrollRectX - (scrollByX * currentZoom);
				newScrollRectY = scrollRectY - (scrollByY * currentZoom);
				// We're not zooming, but we do need to update the bitmap
				// reference position. 
				bmRefPtX -= (scrollByX * currentZoom);
				bmRefPtY -= (scrollByY * currentZoom);

				//See comment in handleLongPress().
				bmRefPtX = clamp(bmRefPtX, scaledWidth / 2.0f, bmLargeImage.getWidth()
						- (scaledWidth / 2.0f));
				bmRefPtY = clamp(bmRefPtY, scaledHeight / 2.0f, bmLargeImage.getHeight()
						- (scaledHeight / 2.0f));
			}

			// Don't let scroll coordinates go outside of the bitmap boundaries. To make
			// sure the right side of the scroll rectangle does not exceed bmWidth, we 
			// must keep the left side of the scroll rectangle inside of (bmWidth - 
			// current_screen_width) where current_screen_width is our display width
			// scaled by our zoom value (= scaledWidth). Same for the height.
			newScrollRectX = clamp(newScrollRectX, 0, bmLargeImage.getWidth()
					- scaledWidth);
			newScrollRectY = clamp(newScrollRectY, 0, bmLargeImage.getHeight()
					- scaledHeight);

			scrollRect.set((int) newScrollRectX, (int) newScrollRectY, (int) newScrollRectX
					+ (int) scaledWidth, (int) newScrollRectY
					+ (int) scaledHeight);
			Paint paint = new Paint();
			canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);

			scrollRectX = newScrollRectX; // reset previous to latest
			scrollRectY = newScrollRectY;
		}

		// Clamp a value between min and max bounds.
		// There are many ways to write this. This way should be easy to
		// understand even if it is not the most concise, and is quite fast.
		private static float clamp(float value, float min, float max) {
			if (value < min)
				value = min;
			else if (value > max)
				value = max;
			return value;
		}

	}
}

//http://www.anddev.org/novice-tutorials-f8/large-image-scrolling-using-low-level-touch-events-t11182-15.html